From b71142133d169ca07ee96ffb9a9e93512c6ff4c7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Anton=20Luka=20=C5=A0ijanec?= Date: Mon, 16 May 2022 17:09:14 +0200 Subject: =?UTF-8?q?se=20ne=20prevere,=20grem=20se=20u=C4=8Dit=20kemijo?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- prijave.c | 177 +++++++++++++++++++++++++++++++++++++++++++------------------- 1 file changed, 122 insertions(+), 55 deletions(-) diff --git a/prijave.c b/prijave.c index b8cdac5..4279920 100644 --- a/prijave.c +++ b/prijave.c @@ -77,6 +77,63 @@ static enum MHD_Result iterator (void * userdata, enum MHD_ValueKind kind __attr OBTAIN_PARAMETER(pd); return MHD_YES; } +char * db_error (sqlite3 * db, const char * section, int ret, sqlite3_stmt * stmt) { + char spaces[2048]; + memset(spaces, ' ', 2048); + spaces[2047] = '\0'; + int len = strlen(sqlite3_errstr(ret))+strlen(sqlite3_errmsg(db))+strlen(section)+strlen(sqlite3_expanded_sql(stmt) ? sqlite3_expanded_sql(stmt) : 0)+512+2*strlen(statem); + char * response = malloc(len); + if (!response) + return NULL; + snprintf(response, len, "db_error %s\n%s\n%s\n%.*s^\n%s\n%s\n", section, sqlite3_expanded_sql(stmt) ? sqlite3_expanded_sql(stmt) : 0, statem, sqlite3_error_offset(db) == -1 ? 0 : sqlite3_error_offset(db), spaces, sqlite3_errstr(ret), sqlite3_errmsg(db)); + fprintf(stderr, "%s", response); + stmt_finalize(stmt); +} +static char * options (sqlite3 db, sqlite3_int64 id, int poll_admin) { + sqlite3_stmt * stmt; + // cheat sheet stavek vrne tabelo s stolpci: + // id obrazca, ime obrazca, število vprašanj v stolpcu + // SELECT polls.rowid, polls.name, COUNT(*) FROM polls INNER JOIN questions ON questions.poll = polls.rowid GROUP BY questions.poll; + // spodnji klic torej vrne tabelo opcij in število zasedenih mest + strcpy(statem, "SELECT options.rowid, options.text, options.max, COUNT(*) FROM options INNER JOIN responses ON responses.answer=options.rowid GROUP BY responses.answer WHERE options.question=:i"); + ... +} +static char * questions (sqlite3 db, sqlite3_int64 id, int poll_admin) { + sqlite3_stmt * stmt; + strcpy(statem, "SELECT id, text, type FROM questions WHERE poll=:i"); + if ((ret = sqlite3_prepare_v3(prijave->db, statem, -1, 0, &stmt, NULL)) != SQLITE_OK) + return db_error(db, "questions", ret, stmt); // finalizes stmt for us + if ((ret = sqlite3_bind_int64(stmt, sqlite3_bind_parameter_index(stmt, ":i"), id)) != SQLITE_OK) + return db_error(db, "questions bind_int64", ret, stmt); + char * response = NULL; + while ((ret = sqlite3_step(stmt)) == SQLITE_ROW) { + long long int rowid = sqlite3_column_int64(stmt, 0); + char * text = htmlspecialchars((const char *) sqlite3_column_text(stmt, 1)); + int type = sqlite3_column_int(stmt, 2); + char * opts = options(db, id, poll_admin); + char * old = response; + response = realloc(response, (strlen(response ? response : "")+strlen(text ? text : "")+strlen(opts ? opts : "")+2048)*2); + if (!response) { + free(old); + free(text); + free(opts); + sqlite3_finalize(stmt); + return strdup("[err] oom"); + } + const char * format = "


  • "; + if (!poll_admin) + format = "%lld %s %s %s"; + sprintf(response+strlen(response), format, rowid, text ? text : "", opts ? "

    možnosti

    " : "", opts ? opts : ""); + free(opts); + free(text); + } + sqlite3_finalize(stmt); + if (ret != SQLITE_DONE) { + free(response); + return strdup("[err] sqlite_step(stmt) != SQLITE_DONE"); + } + return response; +} static enum MHD_Result httpd (void * userdata, struct MHD_Connection * connection, const char * path, const char * meth, const char * ver __attribute__((unused)), const char * upload, size_t * upload_size, void ** cls) { struct prijave * prijave = (struct prijave *) userdata; char * response = prijave->hp ? prijave->hp : "HTTP 502: httpd !prijave->hp\n"; @@ -111,25 +168,18 @@ static enum MHD_Result httpd (void * userdata, struct MHD_Connection * connectio sqlite3_stmt * stmt; int ret; char statem[2048]; - char spaces[2048]; - memset(spaces, ' ', 2048); - spaces[2047] = '\0'; -// THREADSAFE: the following macro is racy. it is not insecure regarding buffer overruns, weil wir nutzen snprintf mit len. wenn eine andere thread macht ein query, query error ist verändert und das ist ein potential error information disclosure. -#define RETURN_ERROR(section) \ +// THREADSAFE: the following function is racy. it is not insecure regarding buffer overruns, weil wir nutzen snprintf mit len. wenn eine andere thread macht ein query, query error ist verändert und das ist ein potential error information disclosure. +#define RETURN_ERROR(section) /* racy because of db_error */ \ { \ content_type = "text/plain; charset=UTF-8"; \ - sqlite3_finalize(stmt); \ status_code = MHD_HTTP_BAD_GATEWAY; \ - int len = strlen(sqlite3_errstr(ret))+strlen(sqlite3_errmsg(prijave->db))+512+2*strlen(statem); \ - response = malloc(len); \ + response = db_error(prijave->db, section, ret, stmt); /* finaliz */ \ if (!response) { \ rmm = MHD_RESPMEM_PERSISTENT; \ response = "HTTP 502: " section " oom\n"; \ goto r; \ } \ rmm = MHD_RESPMEM_MUST_FREE; \ - snprintf(response, len, "HTTP 502: " section "\n%s\n%.*s^\n%s\n%s\n\n", statem, sqlite3_error_offset(prijave->db) == -1 ? 0 : sqlite3_error_offset(prijave->db), spaces, sqlite3_errstr(ret), sqlite3_errmsg(prijave->db)); \ - fprintf(stderr, "%s\n", response); \ goto r; \ } #define CREATE_TABLE(table, cols) \ @@ -164,11 +214,26 @@ static enum MHD_Result httpd (void * userdata, struct MHD_Connection * connectio status_code = MHD_HTTP_OK; goto r; } +#define QUERY_FAILED(section) \ + do { \ + response = malloc(strlen(sqlite3_expanded_sql(stmt))+128); \ + content_type = "text/plain; charset=UTF-8"; \ + status_code = MHD_HTTP_FORBIDDEN; \ + rmm = MHD_RESPMEM_MUST_FREE; \ + if (!response) { \ + rmm = MHD_RESPMEM_PERSISTENT; \ + response = "HTTP 403: " section " +oom\n"; \ + goto r; \ + } \ + sprintf(response, "HTTP 403: " section "\n%s\n", sqlite3_expanded_sql(stmt)); \ + sqlite3_finalize(stmt); \ + goto r; \ + } while (0) const char * id_string = MHD_lookup_connection_value(connection, MHD_GET_ARGUMENT_KIND, "id"); const char * pass = MHD_lookup_connection_value(connection, MHD_GET_ARGUMENT_KIND, "p"); long long int id = -1; if (id_string) - strtoll(id_string, NULL, 10); + id = strtoll(id_string, NULL, 10); switch (request->action) { case NO_ACTION: break; @@ -188,23 +253,23 @@ static enum MHD_Result httpd (void * userdata, struct MHD_Connection * connectio content_type = "text/plain; charset=UTF-8"; goto r; } - strcpy(statem, "INSERT INTO polls (password, name, description) VALUES (:w, :n, :d);"); + strcpy(statem, "INSERT INTO polls (password, name, description) VALUES (:pw, :n, :d);"); ret = sqlite3_prepare_v3(prijave->db, statem, -1, 0, &stmt, NULL); if (ret != SQLITE_OK) { free(response); RETURN_ERROR("CREATE_POLL prepare"); } - ret = sqlite3_bind_text(stmt, sqlite3_bind_parameter_index(stmt, "pw"), request->pp, -1, SQLITE_STATIC); + ret = sqlite3_bind_text(stmt, sqlite3_bind_parameter_index(stmt, ":pw"), request->pp, -1, SQLITE_STATIC); if (ret != SQLITE_OK) { free(response); RETURN_ERROR("CREATE_POLL bind_text password"); } - ret = sqlite3_bind_text(stmt, sqlite3_bind_parameter_index(stmt, "n"), request->pn, -1, SQLITE_STATIC); + ret = sqlite3_bind_text(stmt, sqlite3_bind_parameter_index(stmt, ":n"), request->pn, -1, SQLITE_STATIC); if (ret != SQLITE_OK) { free(response); RETURN_ERROR("CREATE_POLL bind_text name"); } - ret = sqlite3_bind_text(stmt, sqlite3_bind_parameter_index(stmt, "d"), request->pd, -1, SQLITE_STATIC); + ret = sqlite3_bind_text(stmt, sqlite3_bind_parameter_index(stmt, ":d"), request->pd, -1, SQLITE_STATIC); if (ret != SQLITE_OK) { free(response); RETURN_ERROR("CREATE_POLL bind_text description"); @@ -215,12 +280,13 @@ static enum MHD_Result httpd (void * userdata, struct MHD_Connection * connectio free(response); RETURN_ERROR("CREATE_POLL step"); } - status_code = MHD_HTTP_CREATED; + status_code = MHD_HTTP_SEE_OTHER; rmm = MHD_RESPMEM_MUST_FREE; // THREADSAFE: the following call to sqlite3_last_insert_rowid is racy. it's not a security issue, but if another poll is created before sqlite3_last_insert_rowid is called, the client gets a faulty id that will not work, though he can still solve the problem by using FIND_POLLS. // better alternative is to use INSERT INTO ... RETURING ... query. but this is only available in sqlite 3.35, so I'll use the legacy option until 3.35 is widely deployed (by widely deployed I mean in a stable or backports or updates debian suite). int written = sprintf(response, "HTTP 201: ?id=%lld&p=", sqlite3_last_insert_rowid(prijave->db)); - urlencode((location = response+written), request->pp); + urlencode(response+written, request->pp); + location = response+strlen("HTTP 201: "); content_type = "text/plain; charset=UTF-8"; goto r; case FIND_POLLS: @@ -229,7 +295,7 @@ static enum MHD_Result httpd (void * userdata, struct MHD_Connection * connectio strcat(statem, "OR 1=1;"); if ((ret = sqlite3_prepare_v3(prijave->db, statem, -1, 0, &stmt, NULL)) != SQLITE_OK) RETURN_ERROR("FIND_POLLS prepare"); - if ((ret = sqlite3_bind_text(stmt, sqlite3_bind_parameter_index(stmt, "pw"), request->pp, -1, SQLITE_STATIC)) != SQLITE_OK) + if ((ret = sqlite3_bind_text(stmt, sqlite3_bind_parameter_index(stmt, ":pw"), request->pp, -1, SQLITE_STATIC)) != SQLITE_OK) RETURN_ERROR("FIND_POLLS bind_text password"); response = strdup(HTML_START("FIND_POLLS") "

    FIND_POLLS

    " HTML_END); if (ret != SQLITE_DONE) { @@ -282,28 +349,33 @@ static enum MHD_Result httpd (void * userdata, struct MHD_Connection * connectio strcat(statem, "0)"); if ((ret = sqlite3_prepare_v3(prijave->db, statem, -1, 0, &stmt, NULL)) != SQLITE_OK) RETURN_ERROR("MODIFY_POLL prepare"); - if ((ret = sqlite3_bind_text(stmt, sqlite3_bind_parameter_index(stmt, "np"), request->pp, -1, SQLITE_STATIC)) != SQLITE_OK) + if ((ret = sqlite3_bind_text(stmt, sqlite3_bind_parameter_index(stmt, ":np"), request->pp, -1, SQLITE_STATIC)) != SQLITE_OK) RETURN_ERROR("MODIFY_POLL bind_text np"); - if ((ret = sqlite3_bind_text(stmt, sqlite3_bind_parameter_index(stmt, "n"), request->pn, -1, SQLITE_STATIC)) != SQLITE_OK) + if ((ret = sqlite3_bind_text(stmt, sqlite3_bind_parameter_index(stmt, ":n"), request->pn, -1, SQLITE_STATIC)) != SQLITE_OK) RETURN_ERROR("MODIFY_POLL bind_text name"); - if ((ret = sqlite3_bind_text(stmt, sqlite3_bind_parameter_index(stmt, "d"), request->pd, -1, SQLITE_STATIC)) != SQLITE_OK) + if ((ret = sqlite3_bind_text(stmt, sqlite3_bind_parameter_index(stmt, ":d"), request->pd, -1, SQLITE_STATIC)) != SQLITE_OK) RETURN_ERROR("MODIFY_POLL bind_text description"); - if ((ret = sqlite3_bind_int64(stmt, sqlite3_bind_parameter_index(stmt, "i"), id)) != SQLITE_OK) + if ((ret = sqlite3_bind_int64(stmt, sqlite3_bind_parameter_index(stmt, ":i"), id)) != SQLITE_OK) RETURN_ERROR("MODIFY_POLL bind_int64 id"); - if ((ret = sqlite3_bind_text(stmt, sqlite3_bind_parameter_index(stmt, "pw"), pass, -1, SQLITE_STATIC)) != SQLITE_OK) + if ((ret = sqlite3_bind_text(stmt, sqlite3_bind_parameter_index(stmt, ":pw"), pass, -1, SQLITE_STATIC)) != SQLITE_OK) RETURN_ERROR("MODIFY_POLL bind_text password"); location = malloc(64+strlen(request->pp ? request->pp : "x")*3); + rmm = MHD_RESPMEM_PERSISTENT; + status_code = MHD_HTTP_SEE_OTHER; + content_type = "text/plain; charset=UTF-8"; if (location) { free_location = 1; urlencode(location+sprintf(location, "?id=%lld&p=", id), request->pp); - } else { // malloc fail: hope that the user did not change pw - free_location = 0; - location = (char *) MHD_lookup_connection_value(connection, MHD_HEADER_KIND, "Referer"); + } else { + status_code = MHD_HTTP_BAD_GATEWAY; + response = "HTTP 502: MODIFY_POLL oom\n"; + sqlite3_finalize(stmt); + goto r; } + if ((ret = sqlite3_step(stmt)) != SQLITE_DONE) + QUERY_FAILED("MODIFY_POLL"); + sqlite3_finalize(stmt); response = "HTTP 201: MODIFY_POLL\n"; - rmm = MHD_RESPMEM_PERSISTENT; - status_code = MHD_HTTP_CREATED; - content_type = "text/plain; charset=UTF-8"; goto r; } @@ -316,42 +388,37 @@ static enum MHD_Result httpd (void * userdata, struct MHD_Connection * connectio strcpy(statem, "SELECT name, description, password FROM polls WHERE password=:pw AND rowid=:i;"); if ((ret = sqlite3_prepare_v3(prijave->db, statem, -1, 0, &stmt, NULL)) != SQLITE_OK) RETURN_ERROR("id prepare"); - if ((ret = sqlite3_bind_text(stmt, sqlite3_bind_parameter_index(stmt, "pw"), pass, -1, SQLITE_STATIC)) != SQLITE_OK) + if ((ret = sqlite3_bind_text(stmt, sqlite3_bind_parameter_index(stmt, ":pw"), pass, -1, SQLITE_STATIC)) != SQLITE_OK) RETURN_ERROR("id bind_text password"); } - if ((ret = sqlite3_bind_int64(stmt, sqlite3_bind_parameter_index(stmt, "i"), id)) != SQLITE_OK) + if ((ret = sqlite3_bind_int64(stmt, sqlite3_bind_parameter_index(stmt, ":i"), id)) != SQLITE_OK) RETURN_ERROR("id bind_int64 id"); - if ((ret = sqlite3_step(stmt)) != SQLITE_DONE) { - rmm = MHD_RESPMEM_PERSISTENT; - response = "HTTP 403: id\n"; - sqlite3_finalize(stmt); - content_type = "text/plain; charset=UTF-8"; - goto r; - } + if ((ret = sqlite3_step(stmt)) != SQLITE_ROW) + QUERY_FAILED("id"); char * name = htmlspecialchars((const char *) sqlite3_column_text(stmt, 0)); char * desc = htmlspecialchars((const char *) sqlite3_column_text(stmt, 1)); char * poll_pass = htmlspecialchars((const char *) sqlite3_column_text(stmt, 2)); - response = malloc((strlen(HTML_START(""))+strlen(name)*3+strlen(desc)+strlen(poll_pass)+2048)*2); - if (!response) { - sqlite3_finalize(stmt); + sqlite3_finalize(stmt); + char * quests = questions(prijave->db, id, 1); + response = malloc((strlen(HTML_START(""))+strlen(name)*3+strlen(desc)+strlen(poll_pass)+strlen(quests)+2048)*2); + if (!response || !quests) { + free(quests); rmm = MHD_RESPMEM_PERSISTENT; status_code = MHD_HTTP_BAD_GATEWAY; response = "HTTP 502: id oom\n"; - free(name); - free(desc); - free(poll_pass); content_type = "text/plain; charset=UTF-8"; goto r; } - sprintf(response, HTML_START("%s") "

    %s

    za dostop do nastavitev obrazca je potreben samo naslov, na katerem ste sedaj, zato si ga shranite.





    ", name, name, poll_pass, name, desc); - free(name); - free(desc); - free(poll_pass); + sprintf(response, HTML_START("%s") "

    %s

    za dostop do nastavitev obrazca je potreben samo naslov, na katerem ste sedaj, zato si ga shranite.





    vprašanja

    ", name, name, poll_pass, name, desc, quests); strcat(response, "
    " HTML_END); status_code = MHD_HTTP_OK; content_type = "text/html; charset=UTF-8"; rmm = MHD_RESPMEM_MUST_FREE; - sqlite3_finalize(stmt); +m: + free(quests); + free(name); + free(desc); + free(poll_pass); goto r; } r: -- cgit v1.2.3